package spe.ui.editor;

import java.util.EventObject;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.INestableKeyBindingService;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageSelectionProvider;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
import org.eclipse.wst.xml.ui.internal.XMLUIPlugin;
import org.eclipse.wst.xml.ui.internal.tabletree.IDesignViewer;
import org.eclipse.wst.xml.ui.internal.tabletree.XMLMultiPageEditorPart;
import org.eclipse.wst.xml.ui.internal.tabletree.XMLTableTreeHelpContextIds;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import spe.model.notation.SPEMDiagram;
import spe.model.spem.NodeElement;
import spe.model.spem.PackageElement;
import spe.model.spem.SPEMElement;
import spe.model.spem.SPEMFactory;
import spe.model.spem.SPEMFactoryImpl;
import spe.model.spem.SPERootElement;
import spe.xml.SPEXmlAdapter;
import spe.xml.SPEXmlAdapterFactory;
import spe.xml.SPEXmlProvider;

/** @author: Dao Anh Vu	
 *  @description: Editor nay dung de hien thi cac diagram cua SPEM.
 */
@SuppressWarnings({ "restriction"})
public abstract class SPEEditor extends XMLMultiPageEditorPart implements ITabbedPropertySheetPageContributor
{	
	protected IResourceChangeListener resourceChangeListener;
	protected SPESelectionSynchronizer selectionSynchronizer;
	protected SPEActionRegistry actionRegistry;
	protected GraphEditorPage graphPage;
	protected StructuredTextEditor srcPage;
	protected EditDomain editDomain;
	protected CommandStackListener commandStackListener;
	protected ISelectionListener selectionListener;
	protected MenuManager sourceContextMenuManager;
	protected SPEMFactory elementFactory;
	protected SPEXmlProvider contentProvider;
	protected SPEMDiagram diagram = null;
	protected SPEMElement rootSPEMElement = null;
	protected boolean isDirty = false;
	
	@Override
	public void init(IEditorSite site, IEditorInput input) throws PartInitException 
	{
		super.init(site, input);
		initSPEMFactory();
		initActionRegistry();
		initEditDomain();
		initCommandStackListener();
		initSelectionListener();
		initSelectionSynchronizer();
	}
	
	private void initSPEMFactory()
	{
		elementFactory = new SPEMFactoryImpl(this.getEditorId());
	}
	
	private void initEditDomain()
	{
		editDomain = new DefaultEditDomain(this);
	}
	
	private void initCommandStackListener()
	{
		commandStackListener = new CommandStackListener() {
			public void commandStackChanged(EventObject event) {
				handleCommandStackChanged();
			}
		};
		getCommandStack().addCommandStackListener(commandStackListener);
	}
	
	private void handleCommandStackChanged()
	{
		getActionRegistry().updateStackActions();
		if (!isDirty() && getCommandStack().isDirty()) {
			isDirty = true;
			firePropertyChange(IEditorPart.PROP_DIRTY);
		}
	}
	
	protected void initActionRegistry()
	{
		actionRegistry = new SPEActionRegistry(this);
	}
	
	protected void initSelectionListener()
	{
		selectionListener = new ISelectionListener() {
			public void selectionChanged(IWorkbenchPart part,
					ISelection selection) {
				getActionRegistry().updateEditPartActions();
			}
		};
		ISelectionService selectionService = getSite().getWorkbenchWindow()
				.getSelectionService();
		selectionService.addSelectionListener(selectionListener);
	}
	
	protected void initSelectionSynchronizer()
	{
		selectionSynchronizer = new SPESelectionSynchronizer();
	}
	
	protected void loadModelFromSourcePage()
	{
		int pageCount = getPageCount();
		
		for (int i = 0; i < pageCount; i++) 
		{
			if (getEditor(i) instanceof StructuredTextEditor)  
			{
				srcPage = (StructuredTextEditor) getEditor(i);
				break;
				//SPEMElement element = getSPEElement(sourcePage);
			}
		}
		
		if (srcPage != null)
		{
			rootSPEMElement = getSPEElement(srcPage);
			initSourcePageContextMenu(srcPage.getTextViewer().getTextWidget());
		}
		
	}
	
	private SPEMElement getSPEElement(StructuredTextEditor sourcePage)
	{
		Node rootNode = getDocumentElement(sourcePage);
		SPEMElement rootElement = null;
		//SPEMElement rootElement = getSPEMFactory().createElementById("spe.model.SPERootElement");
		
		if(diagram != null){
			rootElement = diagram.getParent();
			SPEXmlAdapterFactory factory = new SPEXmlAdapterFactory(rootNode.getOwnerDocument(), getSPEMFactory(), (SPERootElement) rootElement);
			SPEXmlAdapter adapter = factory.adapt(rootNode);
			System.out.println(String.format("[SPEEditor] RootAdapter: %s; RootElement: %s, rootNode: %s", adapter, rootElement, rootNode));
			adapter.initialize(rootElement);
		}
		//System.out.println(String.format("[SPEEditor] Diagram: %s; RootDiagramNode %s", dg, rootDigramNode));
		return rootElement;
	}
	
	protected Node getDocumentElement(StructuredTextEditor sourcePage)
	{
		Document doc = (Document)sourcePage.getAdapter(Document.class);
		if( doc != null )
			return (Node) doc.getDocumentElement();
		
		return null;
	}
	
	protected void initSourcePageContextMenu(final Control control)
	{
		sourceContextMenuManager = new MenuManager("#PopupMenu");
		sourceContextMenuManager.setRemoveAllWhenShown(true);
		sourceContextMenuManager.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager m) {
				fillContextMenu(m);
			}
		});
		Menu menu = sourceContextMenuManager.createContextMenu(control);
		getSite().registerContextMenu(
				"spe.ui.editor.SourcePage.PopupMenu",
				sourceContextMenuManager, getSite().getSelectionProvider());

		control.setMenu(menu);
	}
	
	protected void fillContextMenu(IMenuManager m)
	{
		m.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	} 
	
	public SPESelectionSynchronizer getSelectionSychronizer()
	{
		return selectionSynchronizer;
	}
	
	public EditDomain getEditDomain()
	{
		return editDomain;
	}
	
	public CommandStack getCommandStack()
	{
		return editDomain.getCommandStack();
	}
	
	protected SPEActionRegistry getActionRegistry()
	{
		return actionRegistry;
	}
	
	public GraphEditorPage getGraphPage()
	{
		return graphPage;
	}
	
	public MyScrollingGraphicalViewer getGraphicalViewer()
	{
		return graphPage.getGraphicalViewer();
	}
	
	public SPEOutlineViewer getOutlineViewer()
	{
		//System.out.println(String.format("[SPEEditor] outlineViewer %s", graphPage.getOutlineViewer()));
		return graphPage.getOutlineViewer();
	}
	
	@Override
	@SuppressWarnings({ "rawtypes" })
	public Object getAdapter(Class adapterClass)
	{
		//System.out.println(String.format("[SPEEditor] adapterClass %s", adapterClass));
		//if (adapterClass.equals(IContentOutlinePage.class))
			//return getOutlineViewer();
		
		if(adapterClass == IPropertySheetPage.class)
			return (new TabbedPropertySheetPage(this));
		
		return super.getAdapter(adapterClass);
	}
	
	public boolean isDirty() 
	{
		return isDirty || super.isDirty();
	}
	
	public void setDirty(boolean dirty)
	{
		isDirty = dirty;
	}

	protected abstract MyScrollingGraphicalViewer createGraphicalViewer(); 
	protected abstract SPEOutlineViewer createOutlineViewer();
	protected abstract SPEXmlProvider createContentProvider();
	protected abstract SPEMDiagram getDiagram();
	public abstract String getEditorId();
	
	public SPEMFactory getSPEMFactory()
	{
		return elementFactory;
	}
	
	public SPEMElement getRootSPEMElement()
	{
		return rootSPEMElement;
	}
	
	@Override
	protected void pageChange(int newPageIndex)
	{
		if (newPageIndex == 0) 
		{
			IKeyBindingService service = getSite().getKeyBindingService();
			if (service instanceof INestableKeyBindingService) {
				INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
				nestableService.activateKeyBindingService(null);
			}
		}
		
		super.pageChange(newPageIndex);
	}
	
	@Override
	public void dispose()
	{
		super.dispose();
	}

	@Override
	public String getContributorId() {
		// TODO Auto-generated method stub
		return null;
	}
}
